/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/


#include"geofence_statemachine.h"

namespace OHOS {
namespace Msdp {
const int32_t TWO = 2;
const int32_t APPROXIMATE_VALUE = 10000;
const int32_t KILOMETER_TO_METER = 1000;
const double ENRTH_RADIUS = 6378.137;

void GeofenceStateMachine::GeofenceCallbackDeathRecipient::OnRemoteDied(const wptr<IRemoteObject>& remote)
{
    if (remote == nullptr) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "OnRemoteDied failed, remote is nullptr");
        return;
    }
    GEOFENCE_HILOGD(GEOFENCE_MODULE_SERVICE, "Recv death notice");
}

bool GeofenceStateMachine::Init()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "GeofenceStateMachine: Init start");
    if (geofenceCBDeathRecipient_ == nullptr) {
        geofenceCBDeathRecipient_ = new GeofenceCallbackDeathRecipient();
    }
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "GeofenceStateMachine: Init success");
    return true;
}

double GeofenceStateMachine::Rad(double d)
{
    return d * M_PI / 180.0;
}

double GeofenceStateMachine::GetDistance(std::vector<double> point1, std::vector<double> point2)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    double lng1 = point1[0];
    double lat1 = point1[1];
    double lng2 = point2[0];
    double lat2 = point2[1];

    double radLat1 = Rad(lat1);
    double radLat2 = Rad(lat2);
    double a = radLat1 - radLat2;
    double b = Rad(lng1) - Rad(lng2);
    double distance = TWO * asin(sqrt(pow(sin(a / TWO), TWO) + cos(radLat1) * cos(radLat2) * pow(sin(b / TWO), TWO)));
    distance = distance * ENRTH_RADIUS;
    distance = round(distance * APPROXIMATE_VALUE);
    distance = distance / APPROXIMATE_VALUE * KILOMETER_TO_METER;

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "distance is %{public}lf", distance);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
    return distance;
}

int32_t GeofenceStateMachine::PointRadiusAlgorithm(std::vector<double> centerPoint, double radius,
    std::vector<double> lbsPoint)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    double distance;
    distance = GetDistance(centerPoint, lbsPoint);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "distance = %{public}lf, radius = %{public}lf", distance, radius);

    if (distance <= radius) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit");
        return 1;
    } else {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit");
        return 0;
    }
}

int32_t GeofenceStateMachine::RayCastingAlgorithm(std::vector<std::vector<double>> poly,
    std::vector<double> lbsPoint)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    double lbsPointx = lbsPoint[0];
    double lbsPointy = lbsPoint[1];
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "lbsPointx = %{public}lf", lbsPointx);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "lbsPointy = %{public}lf", lbsPointy);

    bool stateflag = false;
    int size = poly.size() / TWO;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "size = %{public}d", size);

    double x1 ,x2, y1, y2;

    for (int i = 0, j = size - 1; i < size; j = i, i++)
    {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "i = %{public}d", i);
        x1 = poly[i][0];
        y1 = poly[i][1];
        x2 = poly[j][0];
        y2 = poly[j][1];
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "x1 = %{public}lf", x1);
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "y1 = %{public}lf", y1);
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "x2 = %{public}lf", x2);
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "y2 = %{public}lf", y2);

        if ((x1 == lbsPointx && y1 == lbsPointy) || (x2 == lbsPointx && y2 == lbsPointy)) {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit inside");
            return 1;
        } else if (lbsPointy == y1 && lbsPointy == y2) {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit inside");
        } else if (lbsPointx == x1 && lbsPointy == x2) {
            GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit inside");
        } else {
            double crossX = (lbsPointy - y1) * (x2 - x1) / (y2 - y1) + x1;
            if (crossX == lbsPointx) {
                GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit inside");
                return 1;
            } else if (crossX > lbsPointx) {
                stateflag = !stateflag;
            }
        }
    }

    if (stateflag) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit inside");
        return 1;
    } else {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit outside");
        return 0;
    }
}
} // namespace Msdp
} // namespace OHOS

